#include "MainWindow.h"
#include "ui_MainWindow.h"
#include "SimpleProxyModel.h"
#include "ComplexProxyModel.h"
#include "CampaignViewDelegate.h"



#include <CampaignItem.h>
#include <FileHandler.h>
#include <XmlSerializer.h>
#include <ItemReference.h>



#include <QMessageBox>
#include <QFileDialog>



// unified connections from each tree views
#define connectTreeView(treeView) \
    connect(treeView, SIGNAL(expanded(QModelIndex)), this, SLOT(onTreeViewExpanded(QModelIndex))); \
    connect(treeView, SIGNAL(collapsed(QModelIndex)), this, SLOT(onTreeViewCollapsed(QModelIndex)))



MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    m_model = 0;
    FileHandler::installSerializer(new XmlSerializer(this));

    ui->setupUi(this);

    // tree view connections
    connectTreeView(ui->treeViewSource);
    connectTreeView(ui->treeViewClass);
    connectTreeView(ui->treeViewFeatReqs);
    connectTreeView(ui->treeViewFeats);
    connectTreeView(ui->treeViewSpecials);


    ui->actionNewCharacter->setEnabled(false);

    installEventFilter(this);
}



MainWindow::~MainWindow()
{
    delete ui;
}



bool MainWindow::eventFilter(QObject *o, QEvent *e)
{
    if (o == this && e->type() == QEvent::Close)
    {
        if (!m_model || !m_model->rootItem() || !m_model->rootItem()->isDirty()) return false;

        int result = QMessageBox::question(this,
                                           tr("Save Changes?"),
                                           QString("%1\n%2").arg(tr("There are unsaved changes.")).arg(tr("Do you want to save?")),
                                           QMessageBox::Yes | QMessageBox::No);
        if (result != QMessageBox::Yes) return false;

        on_actionSave_triggered();
        return false;
    }

    return false;
}



void MainWindow::initializeViews()
{
    if (!m_model) return;

    ui->treeViewSource->setModel(m_model);
    ui->treeViewSource->setItemDelegate(new CampaignViewDelegate(this));
    ui->treeViewSource->hideColumn(AbstractItem::IdData);
    ui->treeViewSource->hideColumn(AbstractItem::CategoryData);


    // throw away old proxies
    if (ui->treeViewClass->model()) ui->treeViewClass->model()->deleteLater();
    if (ui->treeViewFeats->model()) ui->treeViewFeats->model()->deleteLater();
    if (ui->treeViewFeatReqs->model()) ui->treeViewFeatReqs->model()->deleteLater();
    if (ui->treeViewSpecials->model()) ui->treeViewSpecials->model()->deleteLater();


    // Class item view
    SimpleProxyModel* classProxy = new SimpleProxyModel(this);

    classProxy->addExcludeFilter("Reference", SimpleProxyModel::EndsWithFilter);
    classProxy->addExcludeFilter("Class Features", SimpleProxyModel::EndsWithFilter); // exclude group
    classProxy->addIncludeFilter("Class Features", SimpleProxyModel::ContainsFilter);
    classProxy->addField(AbstractItem::NameData, "Feature");
    classProxy->setItemModel(m_model);
    ui->treeViewClass->setModel(classProxy);


    // Skill item views
    ComplexProxyModel* skillProxy = new ComplexProxyModel(this);
    //skillProxy->addExcludeFilter("Reference", SimpleProxyModel::EndsWithFilter);
    skillProxy->addIncludeFilter("Skill[.]+", SimpleProxyModel::RegExpFilter); // include items
    skillProxy->addComplexFilter("Reference", ComplexProxyModel::TreeFilter);
    skillProxy->addField(AbstractItem::NameData, "Skill");


    //skillProxy->includeChildren();
    //skillProxy->addChildField();



    skillProxy->setItemModel(m_model);
    ui->treeViewSkills->setModel(skillProxy);



    // Feat item views
    SimpleProxyModel* featProxy = new SimpleProxyModel(this);
    featProxy->addExcludeFilter("Reference", SimpleProxyModel::EndsWithFilter);
    //featProxy->exclude("Feat", SimpleProxyModel::EndsWithFilter); // exclude group
    featProxy->addIncludeFilter("Feat[.]+", SimpleProxyModel::RegExpFilter); // include items

    featProxy->addField(AbstractItem::NameData, "Feat");
    featProxy->setItemModel(m_model);
    ui->treeViewFeats->setModel(featProxy);

    //TreeProxyModel featReqProxy = new TreeProxyModel(this);

    // Special Abilities
    SimpleProxyModel* specialProxy = new SimpleProxyModel(this);
    specialProxy->addExcludeFilter("Reference");
    specialProxy->addExcludeFilter("Special Ability", SimpleProxyModel::EndsWithFilter);
    specialProxy->addIncludeFilter("Special Ability", SimpleProxyModel::ContainsFilter);

    specialProxy->addField(AbstractItem::NameData, "Special Ability");
    specialProxy->setItemModel(m_model);
    ui->treeViewSpecials->setModel(specialProxy);

    onTreeViewExpanded();
}



void MainWindow::onTreeViewExpanded(QModelIndex index)
{
    Q_UNUSED(index);
    ui->treeViewSource->header()->setResizeMode(QHeaderView::ResizeToContents);
    ui->treeViewClass->header()->setResizeMode(QHeaderView::ResizeToContents);
    ui->treeViewFeatReqs->header()->setResizeMode(QHeaderView::ResizeToContents);
    ui->treeViewFeats->header()->setResizeMode(QHeaderView::ResizeToContents);
    ui->treeViewSpecials->header()->setResizeMode(QHeaderView::ResizeToContents);
}



void MainWindow::onTreeViewCollapsed(QModelIndex index)
{
    QTreeView* view = qobject_cast<QTreeView*>(sender());
    if (!view) return;

    QModelIndex parentIndex = view->currentIndex().parent();

    while (parentIndex.isValid())
    {
        if (parentIndex != index)
        {
            parentIndex = parentIndex.parent();
            continue;
        }

        view->setCurrentIndex(index);
        return;
    }
}



void MainWindow::on_actionNewCampaign_triggered()
{
    if (!on_actionClose_triggered()) return;

    m_model = new ItemModel(new CampaignItem(), this);
    initializeViews();
}



void MainWindow::on_actionNewCharacter_triggered()
{
    // not implemented
}



void MainWindow::on_actionOpen_triggered()
{
    if (!on_actionClose_triggered()) return;

    QString file_path = QFileDialog::getOpenFileName(this, tr("Safe As"), QDir::currentPath(), tr("Xml Files (*.xml)"));
    if (file_path.isEmpty()) return;

    ItemRoot* root = FileHandler::open(file_path);
    if (!root) return;

    m_model = new ItemModel(root, this);

    initializeViews();
}



void MainWindow::on_actionSave_triggered()
{
    if (!m_model) return;
    if (FileHandler::save(m_model->rootItem())) return;

    on_actionSave_As_triggered();
}



void MainWindow::on_actionSave_As_triggered()
{
    if (!m_model || !m_model->rootItem()) return;

    QString file_path = QFileDialog::getSaveFileName(this, tr("Safe As"), QDir::currentPath(), tr("Xml Files (*.xml)"));
    if (file_path.isEmpty()) return;

    m_model->rootItem()->setFilePath(file_path);
    FileHandler::save(m_model->rootItem());
}



void MainWindow::on_actionQuit_triggered()
{
    close();
}



bool MainWindow::on_actionClose_triggered()
{
    if (!m_model) return true;

    if (!m_model->rootItem() || !m_model->rootItem()->isDirty())
    {
        m_model->deleteLater();
        m_model = 0;
        return true;
    }

    int result = QMessageBox::question(this,
                                       tr("Save Changes?"),
                                       QString("%1\n%2").arg(tr("There are unsaved changes.")).arg(tr("Do you want to save?")),
                                       QMessageBox::Cancel | QMessageBox::Yes | QMessageBox::No);

    switch (result)
    {
    case QMessageBox::Cancel:
        return false;
    case QMessageBox::No:
        break;
    case QMessageBox::Yes:
        on_actionSave_triggered();
        break;
    default:
        return false;
    }


    m_model->deleteLater();
    m_model = 0;

    return true;
}



void MainWindow::on_actionAdd_triggered()
{
    // not implemented
}



void MainWindow::on_actionDelete_triggered()
{
    if (!m_model) return;

    QModelIndex sourceIndex;
    if (ui->tabWidget->currentWidget() == ui->tabSource)
    {
        sourceIndex = ui->treeViewSource->currentIndex();
    }
    else
    {
        return;
    }

    if (!sourceIndex.isValid()) return;

    QModelIndex nameIndex = sourceIndex.sibling(sourceIndex.row(), AbstractItem::NameData);

    if (m_model->rowCount(sourceIndex))
    {
        QString name = nameIndex.data(Qt::DisplayRole).toString();
        if (name.isEmpty()) name = tr("Item");

        int result = QMessageBox::question(this,
                                           tr("Delete?"),
                                           QString("%1 %2\n%3").arg(name).arg(tr("has child items.")).arg(tr("Delete anyway?")),
                                           QMessageBox::Yes | QMessageBox::No);

        if (result == QMessageBox::No) return;
    }


    // set appropriate current widget
    if (ui->tabWidget->currentWidget() == ui->tabSource)
    {
        ui->treeViewSource->setCurrentIndex(sourceIndex.parent());
    }
    else
    {
        // todo: other tabs
    }

    m_model->removeRow(sourceIndex.row(), sourceIndex.parent());
}



void MainWindow::on_actionCreate_Group_triggered()
{
    if (ui->tabWidget->currentWidget() != ui->tabSource) return;
    if (!m_model) return;

    int row = m_model->rowCount(QModelIndex());
    m_model->insertRow(row, QModelIndex());

    QModelIndex index = m_model->index(row, 0, QModelIndex());
    m_model->setData(index, tr("New Group"), Qt::EditRole);

    ui->treeViewSource->setCurrentIndex(index);
    ui->treeViewSource->edit(index);
}



void MainWindow::on_actionCreate_Item_triggered()
{
    if (ui->tabWidget->currentWidget() != ui->tabSource) return;
    if (!m_model) return;

    QModelIndex currentIndex = ui->treeViewSource->currentIndex();
    if (!currentIndex.isValid()) return;

    QModelIndex categoryIndex = currentIndex.sibling(currentIndex.row(), AbstractItem::CategoryData);
    if (!categoryIndex.isValid()) return;


    QModelIndex parentIndex;
    switch (categoryIndex.data(Qt::EditRole).toInt())
    {
    case ItemCategory_Group:
        parentIndex = currentIndex;
        break;
    case ItemCategory_Item:
        parentIndex = currentIndex.parent();
        break;
    case ItemCategory_Reference:
        parentIndex = currentIndex.parent().parent();
        break;
    default:
        return;
    }

    if (!parentIndex.isValid()) return;

    QModelIndex parentNameIndex = parentIndex.sibling(parentIndex.row(), AbstractItem::NameData);

    if (!m_model) return;

    int row = m_model->rowCount(parentIndex);
    m_model->insertRow(row, parentIndex);

    QModelIndex index = m_model->index(row, 0, parentIndex);
    m_model->setData(index, QString("%1 %2").arg("New").arg(parentNameIndex.data(Qt::DisplayRole).toString()).simplified(), Qt::EditRole);

    ui->treeViewSource->setCurrentIndex(index);
    ui->treeViewSource->edit(index);
}



void MainWindow::on_actionCreate_Reference_triggered()
{
    if (ui->tabWidget->currentWidget() != ui->tabSource) return;
    if (!m_model) return;

    QModelIndex currentIndex = ui->treeViewSource->currentIndex();
    if (!currentIndex.isValid()) return;

    QModelIndex categoryIndex = currentIndex.sibling(currentIndex.row(), AbstractItem::CategoryData);
    if (!categoryIndex.isValid()) return;


    QModelIndex parentIndex;
    switch (categoryIndex.data(Qt::EditRole).toInt())
    {
    case ItemCategory_Group:
        return;
    case ItemCategory_Item:
        parentIndex = currentIndex;
        break;
    case ItemCategory_Reference:
        parentIndex = currentIndex.parent();
        break;
    default:
        return;
    }

    if (!parentIndex.isValid()) return;

    int row = m_model->rowCount(parentIndex);
    m_model->insertRow(row, parentIndex);

    QModelIndex index = m_model->index(row, 0, parentIndex);
    m_model->setData(index, tr("Reference"), Qt::EditRole);

    index = index.sibling(index.row(), ItemReference::RefData);
    ui->treeViewSource->setCurrentIndex(index);
    ui->treeViewSource->edit(index);
}


